use std::str::FromStr;

use chrono::{DateTime as CDateTime, Utc};
use serde::{Deserialize, Serialize};
use wither::{
    bson::{oid::ObjectId, DateTime},
    Model,
};

use crate::AppError;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum BlacklistContent {
    /// ## 表单申请黑名单
    #[serde(rename = "application")]
    Application { club_id: ObjectId },
    /// ## 文章黑名单
    #[serde(rename = "article")]
    Article { section_id: ObjectId },
    #[serde(rename = "unknown")]
    Unknown,
}
impl Default for BlacklistContent {
    fn default() -> Self {
        BlacklistContent::Unknown
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum BlacklistContentDTO {
    #[serde(rename = "application")]
    Application { club_id: String },
    #[serde(rename = "article")]
    Article { section_id: String },
    #[serde(rename = "unknown")]
    Unknown,
}
impl Default for BlacklistContentDTO {
    fn default() -> Self {
        BlacklistContentDTO::Unknown
    }
}
impl From<BlacklistContent> for BlacklistContentDTO {
    fn from(s: BlacklistContent) -> Self {
        match s {
            BlacklistContent::Application { club_id } => Self::Application {
                club_id: club_id.to_string(),
            },
            BlacklistContent::Article { section_id } => Self::Article {
                section_id: section_id.to_string(),
            },
            BlacklistContent::Unknown => Self::Unknown,
        }
    }
}

impl TryFrom<BlacklistContentDTO> for BlacklistContent {
    type Error = AppError;
    fn try_from(value: BlacklistContentDTO) -> Result<Self, Self::Error> {
        Ok(match value {
            BlacklistContentDTO::Application { ref club_id } => Self::Application {
                club_id: ObjectId::from_str(club_id)?,
            },
            BlacklistContentDTO::Article { ref section_id } => Self::Article {
                section_id: ObjectId::from_str(section_id)?,
            },
            BlacklistContentDTO::Unknown => Self::Unknown,
        })
    }
}
/// ## BlacklistModel
/// 黑名单，处于黑名单中的人可能无法执行某些操作
#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct BlacklistModel {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub target: ObjectId,
    pub content: BlacklistContent,
    pub creator: ObjectId,
    pub created_at: DateTime,
}
impl Default for BlacklistModel {
    fn default() -> Self {
        Self {
            id: None,
            target: ObjectId::new(),
            content: Default::default(),
            creator: ObjectId::new(),
            created_at: DateTime::now(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlacklistModelDTO {
    pub id: Option<String>,
    pub target: String,
    pub content: BlacklistContentDTO,
    pub creator: String,
    pub created_at: CDateTime<Utc>,
}

impl From<BlacklistModel> for BlacklistModelDTO {
    fn from(model: BlacklistModel) -> Self {
        Self {
            id: model.id.map(|o| o.to_string()),
            target: model.target.to_string(),
            content: BlacklistContentDTO::from(model.content),
            creator: model.creator.to_string(),
            created_at: model.created_at.to_chrono(),
        }
    }
}
